package com.giggle.mybatis;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.fastjson.JSON;
import com.giggle.mybatis.Interceptor.IdGeneraterInterceptor;
import com.giggle.mybatis.Interceptor.ShardingInterceptor;
import com.giggle.mybatis.condition.InitDataSourceCondition;
import com.giggle.mybatis.condition.InitSessionFactoryCondition;
import com.giggle.mybatis.config.DataSourceProperties;
import com.giggle.mybatis.helper.DruidDataSourceHelper;
import com.github.pagehelper.PageInterceptor;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;

/**
 * @author guozichen
 * @ClassName:
 * @Description: (mybatis自动配置)
 * @date 2020/8/31 9:33
 */
@EnableTransactionManagement
@EnableConfigurationProperties({DataSourceProperties.class,MybatisProperties.class})
@org.springframework.context.annotation.Configuration
public class MybatisAutoConfiguration {
    public static final String TRANSACTION_MANAGER = "dataSourceTransactionManager";
    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

    @Value("${druid.stat.mergeSql:true}")
    private boolean mergeSql;

    @Autowired
    private DataSourceProperties dataSourceProperties;

    @Bean({/*"dataSource",*/"mysqlDataSource"})
    @Conditional({InitDataSourceCondition.class})
    @ConfigurationProperties(prefix = "spring.datasource.mysqldatasource")
    public DataSource mysqlDataSource() throws SQLException {
        return getDataSource();
    }


    protected DataSource getDataSource() throws SQLException {
        logger.info("dataSource init registryvo:{}", JSON.toJSONString(this.dataSourceProperties));
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(this.getDataSourceProperties().getUrl());
        dataSource.setUsername(this.getDataSourceProperties().getUserName());
        dataSource.setPassword(this.getDataSourceProperties().getUserPassword());
        dataSource.setInitialSize(this.getDataSourceProperties().getInitialSize());
        dataSource.setMaxActive(this.getDataSourceProperties().getMaxActive());
        dataSource.setMinIdle(this.getDataSourceProperties().getMinIdle());
        dataSource.setMaxWait((long)this.getDataSourceProperties().getMaxWait());
        dataSource.setValidationQuery(this.getDataSourceProperties().getValidationQuery());
        DruidDataSourceHelper.setDefaultProperty(dataSource);
        String filters = this.getDataSourceProperties().getFilters();
        dataSource.setFilters("mergeStat");
        if (StringUtils.isNotBlank(filters)) {
            dataSource.setFilters("mergeStat," + filters);
        }

        dataSource.setConnectionInitSqls(Lists.newArrayList(new String[]{"set names utf8mb4;"}));
        Properties p = this.setConnectionProperties(this.getDataSourceProperties().getConnectionProperties());
        if (!this.mergeSql) {
            p.setProperty("druid.stat.mergeSql", this.mergeSql + "");
        }

        dataSource.setConnectProperties(p);
        return dataSource;
    }


    public Properties setConnectionProperties(String connectionProperties) {
        Properties properties = new Properties();
        if (StringUtils.isBlank(connectionProperties)) {
            return properties;
        } else {
            String[] entries = connectionProperties.split(";");

            for(int i = 0; i < entries.length; ++i) {
                String entry = entries[i];
                if (entry.length() > 0) {
                    int index = entry.indexOf(61);
                    if (index > 0) {
                        String name = entry.substring(0, index);
                        String value = entry.substring(index + 1);
                        properties.setProperty(name, value);
                    } else {
                        properties.setProperty(entry, "");
                    }
                }
            }

            return properties;
        }
    }


    @Bean({"sqlSessionFactory"})
    @Conditional({InitSessionFactoryCondition.class})
    public SqlSessionFactory sqlSessionFactory(@Qualifier("mysqlDataSource") DataSource mysqlDataSource,
                                               MybatisProperties mybatisProperties) throws Exception {
        return this.getSqlSessionFactory(mysqlDataSource,mybatisProperties);
    }

    protected SqlSessionFactory getSqlSessionFactory(DataSource dataSource,MybatisProperties mybatisProperties) throws Exception {
        Configuration config = new Configuration();
        config.setMapUnderscoreToCamelCase(true);
        config.setLogImpl(Slf4jImpl.class);
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfiguration(config);
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("autoRuntimeDialect", "true");
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageInterceptor.setProperties(properties);
        ShardingInterceptor shardingInterceptor = new ShardingInterceptor();
        IdGeneraterInterceptor idGeneraterInterceptor = new IdGeneraterInterceptor();
        Interceptor[] interceptors = new Interceptor[]{pageInterceptor, idGeneraterInterceptor,shardingInterceptor};
        sqlSessionFactoryBean.setPlugins(interceptors);
        if(mybatisProperties.getMapperLocations() != null && mybatisProperties.getMapperLocations().length >0){
            List<Resource> resources = new ArrayList<>();
            Arrays.stream(mybatisProperties.getMapperLocations()).forEach(e -> {
                    try {
                        resources.addAll(Arrays.asList(new PathMatchingResourcePatternResolver().getResources(e)));
                    }catch (Exception exception){
                logger.error("配置加载出错",exception);
            }});
            Resource[] resourcesArr = new Resource[resources.size()];
            for(int i = 0 ; i< resources.size() ; i++){
                resourcesArr[i] = resources.get(i);
                resourcesArr[i] = resources.get(i);
            }
            sqlSessionFactoryBean.setMapperLocations(resourcesArr);
        }

        if (StringUtils.isNotBlank(mybatisProperties.getTypeAliasesPackage())) {
            sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
        }

        return sqlSessionFactoryBean.getObject();
    }

    @Bean(name = {"dataSourceTransactionManager"})
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("mysqlDataSource") DataSource mysqlDataSource) {
        return new DataSourceTransactionManager(mysqlDataSource);
    }


    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.setServlet(new StatViewServlet());
        servletRegistrationBean.addUrlMappings("/druid/*");
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("resetEnable", "false"); //禁用HTML页面上的“Rest All”功能
//        initParameters.put("allow", "10.8.9.115");  //ip白名单（没有配置或者为空，则允许所有访问）
        initParameters.put("loginUsername", "merchant");  //++监控页面登录用户名
        initParameters.put("loginPassword", "merchant");  //++监控页面登录用户密码
//        initParameters.put("deny", ""); //ip黑名单
        //如果某个ip同时存在，deny优先于allow
        servletRegistrationBean.setInitParameters(initParameters);
        return servletRegistrationBean;
    }

    public DataSourceProperties getDataSourceProperties() {
        return this.dataSourceProperties;
    }

    public void setDataSourceProperties(DataSourceProperties dataSourceProperties) {
        this.dataSourceProperties = dataSourceProperties;
    }
}

